"""
Copyright 2007, Thomas Dejanovic.

This is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.

This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this software; if not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA, or see the FSF site: http://www.fsf.org.
"""

#  This is the most specific level of verilog generation for apb mux
#  hatchlings.  it contains information about how to write verilog
#  from the hatchling data structure.

id = "$Id: hatch_apb_mux_verilog.py 667 2010-07-01 00:12:17Z jayshurtz $"
# $URL: http://hatch.googlecode.com/svn/trunk/hatch/hatch_targets/verilog/obsolete/hatch_apb_mux_verilog.py $
# $Author: jayshurtz $
version = " ".join(id.split()[1:3])

import math
from hatch_verilog import *


# Verilog generation functions.
def get_verilog_port_list(hatchling):
    """ Return a list of verilog ports implied by this hatchling node.
    """
    r = []

    # modified APB slave interface with prefix added.
    prefix = hatchling.property('prefix')

    r.append(["input ", "", "%spclk"%(prefix)])
    r.append(["input ", "", "%spclk_en"%(prefix)])
    r.append(["input ", "", "%sgated_pclk"%(prefix)])
    r.append(["input ", "", "%sgated_pclk_en"%(prefix)])
    r.append(["input ", "", "%spreset_l"%(prefix)])
    r.append(["input ", "", "%spenable"%(prefix)])
    r.append(["input ", "", "%spsel"%(prefix)])
    r.append(["input ", "", "%spwrite"%(prefix)])
    r.append(["input ", "[31:0]", "%spwdata"%(prefix)])
    r.append(["output", "[31:0]", "%sprdata"%(prefix)])
    r.append(["input ", "[31:2]", "%spaddr"%(prefix)])

    # now generate a port list for each slave interface.  There is a
    # bit of Oddness here in that the prefix is actually the name of
    # the port.
    for c in hatchling.contentList:
        prefix = c.name
        targetBus = c.hatchling.property('target_bus')
        addrWidth = targetBus.property('addr_width')
        r.append(["output", "", "%spclk"%(prefix)])
        r.append(["output", "", "%spclk_en"%(prefix)])
        r.append(["output", "", "%sgated_pclk"%(prefix)])
        r.append(["output", "", "%sgated_pclk_en"%(prefix)])
        r.append(["output", "", "%spreset_l"%(prefix)])
        r.append(["output", "", "%spenable"%(prefix)])
        r.append(["output", "", "%spsel"%(prefix)])
        r.append(["output", "", "%spwrite"%(prefix)])
        r.append(["output", "[31:0]", "%spwdata"%(prefix)])
        r.append(["input ", "[31:0]", "%sprdata"%(prefix)])
        r.append(["output", "[%d:2]"%(addrWidth+1), "%spaddr"%(prefix)])

    return r


def get_verilog_declaration_list(hatchling):
    """ Return a list of declarations required to build this hatchling node.
    """
    r = []

    # just to recap, we are building an apb mux which does address
    # decoding for the modules connected to it.

    # APB Interface Timing.
    # 
    #              ___     ___     ___    
    # clk      ___/   \___/   \___/   \___
    #                       _______
    # penable  ____________/       \______
    #               _______________
    # psel     ____/               \______
    #               _______________
    # paddr    XXXXX_______________XXXXXXX
    #               _______________
    # pwdata   XXXXX_______________XXXXXXX
    #                       _______
    # prdata   XXXXXXXXXXXXX_______XXXXXXX
    #
    prefix = hatchling.property('prefix')
    r.append(["wire", "", "%spclk"%(prefix)])
    r.append(["wire", "", "%spclk_en"%(prefix)])
    r.append(["wire", "", "%sgated_pclk"%(prefix)])
    r.append(["wire", "", "%sgated_pclk_en"%(prefix)])
    r.append(["wire", "", "%spreset_l"%(prefix)])
    r.append(["wire", "", "%spenable"%(prefix)])
    r.append(["wire", "", "%spsel"%(prefix)])
    r.append(["wire", "", "%spwrite"%(prefix)])
    r.append(["wire", "[31:0]", "%spwdata"%(prefix)])
    r.append(["wire", "[31:0]", "%sprdata"%(prefix)])
    r.append(["wire", "[31:2]", "%spaddr"%(prefix)])

    for c in hatchling.contentList:
        prefix = c.name
        targetBus = c.hatchling.property('target_bus')
        addrWidth = targetBus.property('addr_width')
        type = c.hatchling.property('type')
        r.append(["wire", "", "%spclk"%(prefix)])
        r.append(["wire", "", "%spclk_en"%(prefix)])
        r.append(["wire", "", "%sgated_pclk"%(prefix)])
        r.append(["wire", "", "%sgated_pclk_en"%(prefix)])
        r.append(["wire", "", "%spreset_l"%(prefix)])
        r.append(["wire", "", "%spenable"%(prefix)])
        r.append(["wire", "", "%spsel"%(prefix)])
        r.append(["wire", "", "%spwrite"%(prefix)])
        r.append(["wire", "[31:0]", "%spwdata"%(prefix)])
        r.append(["wire", "[31:0]", "%sprdata"%(prefix)])
        r.append(["wire", "[%d:2]"%(addrWidth+1), "%spaddr"%(prefix)])

    return reduce_declaration_list(r)


def generate_verilog(hatchling, moduleName):
    """ Write out the complete verilog module description required to
        implement the mux in the hatch datastructure.
    """
    # attempt to open a file for writing.
    fileName = moduleName + ".v"
    outputFile = open(fileName, 'w')
    targetBus = hatchling.property('target_bus')

    # some debug.
    # print hatchling
    # print "address range %d"%(targetBus.workingAddrRange)

    # write out the header.
    outputFile.write(verilogHeader)
    if hatchling.has_property('header'):
        outputFile.write(hatchling.property('header'))

    # build the module definition
    outputFile.write("\nmodule %s\n"%(moduleName))

    # so get the list of ports we need to make this module.
    portList  = get_verilog_port_list(hatchling)

    # check the port list as we generate the module port list.
    portHash = {}
    s = "  ("
    for (m, w, n) in portList[:-1]:
        s += "%s, "%(n)
        if len(s) > 60:
            outputFile.write("%s\n"%(s))
            s = "   "
        if portHash.has_key(n):
            raise "*** ERROR - port %s is multiply defined in hatchfile %s."%(n, moduleName)
        else:
            portHash[n] = 1
    (m, w, n) = portList[-1]
    s += "%s);\n\n\n"%(n)
    if portHash.has_key(n):
        raise "*** ERROR - port %s is multiply defined in hatchfile %s."%(n, moduleName)
    del portHash
    outputFile.write(s)

    # now write out all the port mode definitions.
    outputFile.write("  //---------------------------------------------------------------------\n")
    outputFile.write("  // port mode definition\n\n")
    for (m, w, n) in portList:
        s = "  %s %s"%(m, w)
        s += " " * (20 - len(s))
        outputFile.write("%s %s;\n"%(s, n))
    outputFile.write("\n\n")

    # now write out all the wires and reg definitions required by this module.
    declarationList = get_verilog_declaration_list(hatchling)
    outputFile.write("  //---------------------------------------------------------------------\n")
    outputFile.write("  // register and wire definitions\n\n")
    for (m, w, n) in declarationList:
        # wires declared with a width of None imply the width is
        # unknown and are a place holder for a wire or register
        # declaration somewhere else in the design.  if any wires of
        # width None are found when writing out the design, it imples
        # that a signal is used in some expression that is not
        # preovided in the register definition file.
        if w == None:
            raise "*** ERROR - %s %s is required to implement some function but is not driven by anything."%(m, n)
        s = "  %s %s"%(m, w)
        s += " " * (20 - len(s))
        outputFile.write("%s %s;\n"%(s, n))
    outputFile.write("\n\n")

    # Generate the decode for each module.
    localPrefix = hatchling.property('prefix')
    localBaseAddr  = hatchling.property('base_address')
    localAddrRange = hatchling.property('address_range')
    localMaskBits  = int(math.ceil(math.log(localAddrRange) / math.log(2)))
    localMask      = 2 ** localMaskBits - 1L
    outputFile.write("\n  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n")
    for c in hatchling.contentList:
        addrWidth  = targetBus.property('addr_width')
        baseAddr   = c.get_base_address()
        addrRange  = c.get_address_range()
        maskBits   = int(math.ceil(math.log(addrRange) / math.log(2)))
        decodeAddr = (baseAddr & localMask) >> maskBits
        portPrefix = c.name
        outputFile.write("  assign %spsel = (%spaddr[%d:%d] == %d'h%x) & %spsel;\n"%(portPrefix, \
            localPrefix, localMaskBits, maskBits, localMaskBits - maskBits + 1, decodeAddr, localPrefix))
        outputFile.write("  assign %spenable = %spenable;\n\n"%(portPrefix, localPrefix))

    # now the write data and address ports.
    outputFile.write("\n  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n")
    for c in hatchling.contentList:
        ctb = c.hatchling.property('target_bus')
        addrWidth  = ctb.property('addr_width')
        portPrefix = c.name
        outputFile.write("  assign %spaddr  = %spaddr[%d:2];\n"%(portPrefix, localPrefix, addrWidth + 1))
        outputFile.write("  assign %spwdata = %spwdata;\n"%(portPrefix, localPrefix))
        outputFile.write("  assign %spwrite = %spwrite;\n\n"%(portPrefix, localPrefix))

    # now the read mux.
    outputFile.write("\n  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n")
    s = "  assign %sprdata ="%(localPrefix)
    pad = ""
    outputFile.write(s)
    
    for c in hatchling.contentList:
        portPrefix = c.name
        outputFile.write("%s %spsel ? %sprdata : \n"%(pad, portPrefix, portPrefix))
        pad = " " * len(s)
    outputFile.write("%s 32'd0;\n"%(pad))

    # now connect up and buffer some control signals.
    outputFile.write("\n  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n")
    for c in hatchling.contentList:
        portPrefix = c.name
        outputFile.write("  assign %spclk          = %spclk;\n"%(portPrefix, localPrefix))
        outputFile.write("  assign %spclk_en       = %spclk_en;\n"%(portPrefix, localPrefix))
        outputFile.write("  assign %sgated_pclk    = %sgated_pclk;\n"%(portPrefix, localPrefix))
        outputFile.write("  assign %sgated_pclk_en = %sgated_pclk_en;\n"%(portPrefix, localPrefix))
        outputFile.write("  assign %spreset_l      = %spreset_l;\n\n"%(portPrefix, localPrefix))

    # done!
    outputFile.write("\n  //---------------------------------------------------------------------\n\n")
    outputFile.write("endmodule\n")
    outputFile.close()


